package com.ec50n9.server.module.issue

import com.ec50n9.server.common.mayBeAlreadyExist
import com.ec50n9.server.common.mayBeNotFound
import com.ec50n9.server.module.comment.CommentCreateForm
import com.ec50n9.server.module.comment.CommentDetailRendered
import com.ec50n9.server.module.comment.CommentListRendered
import com.ec50n9.server.module.comment.CommentService
import com.ec50n9.server.module.common.PageRendered
import com.ec50n9.server.module.common.renderTo
import com.ec50n9.server.module.issueCategory.IssueCategoryService
import com.ec50n9.server.module.user.UserService
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.stereotype.Service
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*

interface IssueRepository : PagingAndSortingRepository<Issue, String> {
    fun findAllByOrderByLaunchedAtDesc(): Iterable<Issue>
}

@Service
class IssueService(private val issueRepository: IssueRepository) {

    private val name = "问题"

    fun findOne(id: String): Issue = mayBeNotFound(name) {
        issueRepository.findById(id).orElseThrow()
    }

    fun findAllWithPaging(page: Int?, size: Int?, sortBy: String?) = issueRepository.findAll(
        PageRequest.of(
            (page ?: 1) - 1, size ?: 10, Sort.by(sortBy ?: "id").ascending()
        )
    )

    fun findAll() = issueRepository.findAllByOrderByLaunchedAtDesc()

    fun newOne(issue: Issue) = mayBeAlreadyExist(name) {
        issueRepository.save(issue)
    }

    fun update(issue: Issue) = issueRepository.save(issue)

    fun delete(issue: Issue) = issueRepository.delete(issue)
}

@Tag(name = "问题管理")
@RestController
@RequestMapping("/api/issues")
class IssueController(
    private val issueService: IssueService,
    private val issueCategoryService: IssueCategoryService,
    private val userService: UserService,
    private val commentService: CommentService
) {

    @Operation(summary = "获取全部")
    fun findAll(): List<IssueListRendered?> =
        issueService.findAll().renderTo(IssueListRendered::class.java)

    @GetMapping
    fun findAllWithPagingAndSorting(
        page: Int?,
        size: Int?,
        sortBy: String?
    ): PageRendered<IssueListRendered> =
        PageRendered(
            issueService.findAllWithPaging(page, size, sortBy)
                .map { issue -> issue.renderTo(IssueListRendered::class.java) }
        )

    @Operation(summary = "通过id获取")
    @GetMapping("/{id}")
    fun findOne(@PathVariable id: String): IssueDetailRendered? =
        issueService.findOne(id).renderTo(IssueDetailRendered::class.java)

    @Operation(summary = "新建")
    @PostMapping
    fun newOne(@RequestBody @Validated createForm: IssueCreateForm): IssueDetailRendered =
        issueService.newOne(createForm.renderTo()).renderTo(IssueDetailRendered::class.java)

    @Operation(summary = "更新")
    @PatchMapping("/{id}")
    fun update(@RequestBody updateForm: IssueUpdateForm, @PathVariable id: String): IssueDetailRendered? {
        val issue = issueService.findOne(id)
        updateForm.renderTo(issue)
        return issueService.update(issue).renderTo(IssueDetailRendered::class.java)
    }

    @Operation(summary = "点赞")
    @PostMapping("/{id}/vote")
    fun voteIt(@PathVariable id: String) {
        val issue = issueService.findOne(id)
        issue.voteCount++
        issueService.update(issue)
    }

    @Operation(summary = "删除")
    @DeleteMapping("/{id}")
    fun delete(@PathVariable id: String) = with(issueService) {
        delete(findOne(id))
    }

    // 评论管理

    @GetMapping("/{id}/comments")
    fun getComments(@PathVariable id: String): List<CommentListRendered> {
        return issueService.findOne(id).comments.renderTo(CommentListRendered::class.java)
    }

    @GetMapping("/{id}/comments/{commentId}")
    fun getCommentsById(@PathVariable id: String, @PathVariable commentId: String): CommentDetailRendered {
        return commentService.findOne(commentId).renderTo(CommentDetailRendered::class.java)
    }

    @PostMapping("/{id}/comments")
    fun newComment(
        @PathVariable id: String,
        @RequestBody @Validated createForm: CommentCreateForm
    ): CommentDetailRendered {
        val comment = commentService.newOne(createForm.renderTo())
        val issue = issueService.findOne(id)
        issue.comments.add(comment)
        issueService.update(issue)
        return comment.renderTo(CommentDetailRendered::class.java)
    }

    @DeleteMapping("/{id}/comments/{commentId}")
    fun deleteComment(@PathVariable id: String, @PathVariable commentId: String) = with(commentService) {
        delete(findOne(commentId))
    }
}